﻿/// <reference path="enumerations.ts" />

module Supernova {

    export class Ship {

        public code: number = 0;
        public preset: ShipPreset = null;
        public name: string = 'Unnamed Class';
        public owner: OwnerType = OwnerType.none;
        public cost: number = 0;
        public speed: number = 0;
        public maxSpeed: number = 0;
        public armor: number = 0;
        public maxArmor: number = 0;
        public shields: number = 0;
        public maxShields: number = 0;
        public attack: number = 0;
        public attackRadius: number = 0;
        public attackWait: number = 0;
        public maxAttackWait: number = 0;
        public bombard: number = 0;
        public bombardWait: number = 0;
        public maxBombardWait: number = 0;
        public artilleryPods: number = 0;
        public artilleryPodWait: number = 0;
        public scanning: number = 0;
        public population: number = 0;
        public terraforming: number = 0;
        public terraformingWait: number = 0;
        public miningRate: number = 0;
        public miningAmount: number = 0;
        public miningProgress: number = 0;
        public visible: boolean = true;
        public formation: boolean = false;
        public missiles: Array<Missile> = [];

        public currentLocation: Location = null;
        public orders: Array<ShipOrder> = [];
        public currentOrder: ShipOrder = null;

        public calculated: {
            x: number;
            y: number;
            isLanded: boolean;
            canStop: boolean;
            canTerraform: boolean;
            canColonise: boolean;
            canBombard: boolean;
            canExplore: boolean;
            canRepair: boolean;
            canDisband: boolean
        } = {
            x: -1,
            y: -1,
            isLanded: false,
            canStop: false,
            canTerraform: false,
            canColonise: false,
            canBombard: false,
            canExplore: false,
            canRepair: false,
            canDisband: false
        };

        clearOrders() {
            this.orders.length = 0;
            this.currentOrder = null;
        }

        calculate() {
            var landedBody = this.currentLocation.body;
            var landedSystem = landedBody === null ? null : landedBody.system;
            var landedOwner = landedSystem === null ? OwnerType.none : landedSystem.owner;
            var contested = landedOwner != OwnerType.none && landedOwner != this.owner;
            this.calculated.canStop = this.currentOrder !== null;
            this.calculated.canTerraform = this.terraforming > 0 && this.terraformingWait <= 0 && landedBody !== null && (landedBody.type == BodyType.barren || (landedBody.type == BodyType.gas && galaxy.research.actions.terraformGasGiants)) && !contested;
            this.calculated.canColonise = this.population > 0 && landedBody !== null && landedBody.type == BodyType.habitable && landedBody.population + 1 <= landedBody.maxPopulation && !contested;
            this.calculated.canBombard = this.bombard > 0 && this.bombardWait <= 0 && landedBody !== null && landedBody.population > 0 && contested;
            this.calculated.canExplore = (this.currentOrder === null || this.currentOrder.type != ShipOrderType.explore) && galaxy.research.actions.explore;
            this.calculated.canRepair = (this.currentOrder === null || this.currentOrder.type != ShipOrderType.repair) && this.armor < this.maxArmor && galaxy.research.actions.repair === true;
            this.calculated.canDisband = this.owner == OwnerType.human;
            this.calculated.isLanded = this.currentLocation.body !== null;
        }

        isMining(): boolean {
            return this.currentOrder === null && this.miningRate > 0 && this.calculated.isLanded && this.currentLocation.body.gold > 0;
        }

        isRepairing(): boolean {
            return this.currentOrder !== null && this.currentOrder.type == ShipOrderType.repair;
        }

        isComputerControlled(): boolean {
            return galaxy.mode == GameMode.singlePlayer && this.owner <= OwnerType.computer;
        }

        damage(attacker: Ship, amount: number, useShields: boolean) {

            // Apply damage to the ship, absorbing it in shields if necessary
            if (useShields) {
                this.shields -= amount;
                if (this.shields <= 0) {
                    this.armor -= (-this.shields);
                    this.shields = 0;
                }
            } else {
                this.armor -= amount;
            }

            // Ensure armor cannot drop below zero
            if (this.armor <= 0) {
                this.armor = 0;
            }

            // Play appropriate sound
            if (this.armor > 0) {
                if (this.shields > 0 && useShields) {
                    Sounds.play(Sounds.items.missileHitShield);
                } else {
                    Sounds.play(Sounds.items.missileHit);
                }
            } else {

                // Communicate destruction of ship to other players
                if (this.owner != OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                    channel.invoke.destroy(this.code);
                }

                // Notify effect subsystem
                galaxy.effects.execute((e: Effect) => { return e.shipDestroyed !== undefined },
                    (e: Effect) => { e.shipDestroyed(attacker, this); });
            }
        }

        terraformAction(body: Body, resourcesUsed: number = undefined) {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canTerraform) return;
            }

            // Terraforming of gas giants is a special case
            if (body.type == BodyType.gas) {
                body.type = BodyType.habitable;
                body.maxPopulation = randomInt(1, MAX_BODY_POPULATION_GAS);
                if (galaxy.mode == GameMode.multiplayer) {
                    channel.invoke.updateBodyType(body.system.code, body.code, body.type);
                    channel.invoke.updateBodyMaxPopulation(body.system.code, body.code, body.maxPopulation);
                }
            }

            // Communicate action to other players
            if (resourcesUsed === undefined) {
                var resourcesUsed = galaxy.research.passive.efficientTerraforming ? 0.125 : 0.5;
            }
            if (this.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                Sounds.play(Sounds.items.terraform);
            }
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.terraform(this.code, body.system.code, body.code, resourcesUsed);
            }

            // Terraform
            body.type = BodyType.habitable;
            this.terraforming -= resourcesUsed;

            // Notify effect subsystem
            galaxy.effects.execute((e: Effect) => { return e.shipActionPerformed !== undefined },
                (e: Effect) => { e.shipActionPerformed(this, ShipAction.terraform); });
        }

        coloniseAction(body: Body) {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canColonise) return;
            }

            // Communicate action to other players
            if (this.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                Sounds.play(Sounds.items.colonise);
            }
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.colonise(this.code, body.system.code, body.code);
            }

            // Colonise
            body.system.owner = this.owner;
            body.population += 1;
            this.population -= 1;

            // Notify effect subsystem
            galaxy.effects.execute((e: Effect) => { return e.shipActionPerformed !== undefined },
                (e: Effect) => { e.shipActionPerformed(this, ShipAction.colonise); });
        }

        bombardAction(body: Body) {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canBombard) return;
            }

            // Communicate action to other players
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.bombard(this.code, body.system.code, body.code, body.population);
            }

            // Decide whether to remove orbital or perform a proper invasion
            this.bombardWait = this.maxBombardWait;
            if (body.facilities.orbitals > 0) {
                this.damage(this, this.isComputerControlled() ? this.armor : config.damageTakenOrbital, false);
                body.facilities.orbitals--;
                if (this.owner == OwnerType.human || body.system.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                    Sounds.play(Sounds.items.bombardOrbital);
                }
            } else {
                this.damage(this, this.isComputerControlled() ? this.armor : config.damageTakenInvade, false);
                body.population -= this.bombard;
                if (this.owner == OwnerType.human || body.system.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                    Sounds.play(Sounds.items.bombardPlanet);
                }
            }

            // Has the body been cleared?
            if (body.population < 1) body.depopulate();

            // Add messages
            var systemPopulation = body.system.calculatePopulation();
            if (body.system.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                if (systemPopulation <= 0) {
                    Notices.addMessage(MessageType.fatal, 'System ' + body.system.name + ' has been completely depopulated!');
                } else {
                    Notices.addMessage(MessageType.fatal, 'System ' + body.system.name + ' is being bombarded!');
                }
            }

            // Make neutral system if completely depopulated
            if (systemPopulation <= 0) body.system.depopulate();

            // Notify effect subsystem
            galaxy.effects.execute((e: Effect) => { return e.shipActionPerformed !== undefined },
                (e: Effect) => { e.shipActionPerformed(this, ShipAction.bombard); });
        }

        repairAction() {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canRepair) return;
            }

            // Communicate action to other players
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.repair(this.code, this.armor == 0 ? 1 : this.armor);
            }

            // Start repairing
            this.setSingleOrder(ShipOrder.create(ShipOrderType.repair));
        }

        disbandAction() {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canDisband) return;
            }

            // Communicate action to other players
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.disband(this.code);
            }

            // Disband
            if (galaxy.mode != GameMode.replay && this.owner == OwnerType.human) {
                if (galaxy.research.passive.reclaim === true) {
                    var cost = Math.floor(this.cost / 2 * (this.armor / this.maxArmor));
                    galaxy.gold += cost;
                    Notices.addToast(this.currentLocation.x, this.currentLocation.y, '+ ◍ ' + cost);
                    Sounds.play(Sounds.items.collectGold);
                } else {
                    Sounds.play(Sounds.items.disband);
                }
            }
            if (galaxy.mode == GameMode.replay) {
                Sounds.play(Sounds.items.disband);
            }
            this.die(false);

            // Notify effect subsystem
            galaxy.effects.execute((e: Effect) => { return e.shipActionPerformed !== undefined },
                (e: Effect) => { e.shipActionPerformed(this, ShipAction.disband); });
        }

        exploreAction() {

            // Give ship order to explore
            this.setSingleOrder(ShipOrder.create(ShipOrderType.explore));
        }

        stopAction() {

            // Verify the action is permitted if not invoked via comms
            if (this.owner == OwnerType.human) {
                this.calculate();
                if (!this.calculated.canStop) return;
            }

            // Communicate action to other players
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.stop(this.code, this.currentLocation.x, this.currentLocation.y);
            }

            // Stop
            this.speed = 0;
            this.clearOrders();
        }

        launchArtillery(targetBody: Body, strength: number, ignoreOrbitals: boolean) {

            // Create a new artillery shell
            this.artilleryPods--;
            this.artilleryPodWait = this.owner == OwnerType.human ? galaxy.research.artillery.ship.wait : ARTILLERY_DEFAULT_WAIT_SHIP;
            var shell = new Artillery(null, this, targetBody);
            shell.strength = strength;
            shell.ignoreOrbitals = ignoreOrbitals;
            galaxy.artillery.push(shell);

            // Communicate action to other players
            if (this.owner == OwnerType.human && galaxy.mode == GameMode.multiplayer) {
                channel.invoke.artilleryShipBody(this.code, targetBody.system.code, targetBody.code, strength, ignoreOrbitals);
            }

            // Play sound if origin or destination body is human
            if (this.owner == OwnerType.human || targetBody.system.owner == OwnerType.human || galaxy.mode == GameMode.replay) {
                Sounds.play(Sounds.items.artillery);
            }
        }

        setSingleOrder(order: ShipOrder) {
            this.speed = 0;
            this.clearOrders();
            this.orders.push(order);
        }

        updateStats() {
            this.population = config.slotData.colony[this.preset.population];
            this.terraforming = this.preset.terraform;
            this.scanning = config.slotData.scanner[this.preset.scanning];
            this.maxSpeed = config.slotData.speeds[this.preset.speed];
            this.maxArmor = config.slotData.armor[this.preset.armor];
            this.armor = this.maxArmor;
            this.maxShields = config.slotData.shields[this.preset.shields];
            this.shields = this.maxShields;
            this.attack = config.slotData.attackPower[this.preset.attack];
            this.attackRadius = config.slotData.attackRadius[this.preset.attack];
            this.attackWait = 0;
            this.maxAttackWait = config.slotData.attackDelay[this.preset.attack];
            this.bombard = config.slotData.bombardPower[this.preset.bombard];
            this.maxBombardWait = config.slotData.bombardDelay[this.preset.bombard];
            this.miningRate = config.slotData.miningRate[this.preset.mining];
            this.miningAmount = config.slotData.miningAmount[this.preset.mining];
            if (this.scanning < this.attackRadius + config.scannerMinIncrementOnAttackRadius) {
                this.scanning = this.attackRadius + config.scannerMinIncrementOnAttackRadius;
            }
        }

        public die(explode: boolean) {
            var i = galaxy.ships.indexOf(this);
            if (i != -1) {

                // Effects
                if (explode) {
                    addParticles(this.currentLocation.x, this.currentLocation.y, 30, 20, 4, 2, '#fff');
                    if (this.owner == OwnerType.human && galaxy.mode != GameMode.replay) {
                        Sounds.play(Sounds.items.shipExplodeMe);
                    } else {
                        Sounds.play(Sounds.items.shipExplodeEnemy);
                    }
                }

                // Delete the ship
                galaxy.ships.splice(i, 1);
                var index = selected.ships.indexOf(this);
                if (index != -1) {
                    selected.ships.splice(index, 1);
                }
            }
        }

        public static create(body: Body, preset: ShipPreset, pods: number, owner: OwnerType, name: string): Ship {

            // Create ship at specified body with specified preset details, owner and name
            var ship = new Ship();
            ship.owner = owner;
            ship.name = name;
            ship.currentLocation = new Location();
            ship.currentLocation.setBody(body);
            ship.preset = preset;
            ship.artilleryPods = pods;
            ship.updateStats();

            // Decrement origin body population
            body.population -= ship.population;

            // Add to galaxy
            galaxy.ships.push(ship);
            return ship;
        }

    }

}